# -*- coding: utf-8 -*-
from numpy import *
import  matplotlib
import matplotlib.pyplot as plt
import classify

'''
本例中使用了一组样本数据,样本文件位于/Users/ting/Documents/51code/git/python/KNN/datingTestSet2.txt
该数据为某女士整理的与其约过会的1000名男士的数据，该女士将约会的男士类型分为三种:
1.不喜欢的人;
2.魅力一般的人;
3.极具魅力的人;

同时通过以下三项指标来判断约会的男士是属于哪一种类型:
1.每年获得的飞行常数里程数;
2.玩视频游戏所耗费的时间比;
3.每周消费的冰激凌公升数;

样本示例如下：
40920	8.326976	0.953952	3
14488	7.153469	1.673904	2
26052	1.441871	0.805124	1


目标：
通过分析样本数据，产生生成一个简单的算法，能够通过输入一些特征数据，判断该男生是否是自己喜欢的类型.

思路分析：
从条件来看 该问题属于监督学习中的分类问题（有样本数据且样本数据有明确的结果，结果是离散的）
1.对文件中的样本数据进行预处理，转化成程序可以接受的格式
2.对特征数据进行归一化处理,因为三种特征对于该女士来说是同等重要的，但由于三种特征的取值范围和单位不同，对方差的结果影响会很大，
比如飞行里程和玩游戏时间比例的差距会很大，但二者的重要性是一样的，所以需要对数据进行归一化处理，将特征数据的取值范围统一在0~1或者-1~1之间
4.利用编写好的classify0分类器 对样本数据进行分类
5.测试验证， 通常，将样本数据的90%用于训练分类算法，剩余10%用作验证


优点：
1.简单 易于实现
2.识别效果相对较好

缺点：
1.当训练样本数据很大的时候，需要计算每个测试样本和训练样本直接的距离，计算量很大，很耗时,占用存储空间


特点：
该算法并不考虑样本数据的实际含义，仅仅从数据特征的角度寻找一种对应关系，该特点虽然简化了数据的分析处理，但是也使得我们无法去分析数据的实际含义，而有时候我们恰恰需要这么做

'''


'''
该方法用于编写一个简易的性格检测系统，通过在界面输入相关参数，测定性格类型
'''
def dating_person_classify():
    result_list=['不喜欢的人','魅力一般的人','极具魅力的人']
    plane_miles=float(input("您每年累计的航空里程是多少?\n"))
    ice_cream=float(input("您每周吃多少公式的冰激凌?\n"))
    games_times=float(input("您每年玩游戏所耗费的时间比是多少？\n"))
    dating_data_mat, dating_labels = file2matrix("/Users/ting/Documents/51code/git/python/KNN/datingTestSet2.txt")
    normal_matrix, ranges, min_values = auto_normalize(dating_data_mat)
    input_data=array([plane_miles,ice_cream,games_times])
    input_data=(input_data-min_values)/ranges
    classify_result = classify.classify0(input_data,normal_matrix,dating_labels, 4)

    print ("经检测，您是属于她感觉",result_list[classify_result-1])





'''
该方法用于预处理从文件中导入的样本数据 
'''
def file2matrix(file_name):
    fr=open(file_name)
    array_lines=fr.readlines()
    member_of_lines=len(array_lines) #获取行数
    return_mat=zeros((member_of_lines,3)) # 创建一个memberOfLines行 3列 元素均为0的矩阵
    class_label_vector=[] #创建一个空列表
    index=0
    for line in array_lines:
        line=line.strip()  #删除字符串中开头、结尾处，位于 rm删除序列的字符 rm为strip()的传入参数 为空时表示删除空格和回车
        list_from_line=line.split('\t')  #按照'\t'字符对数据进行分割 返回一个列表
        return_mat[index,:]=list_from_line[0:3] #将listFromLine中角标为0到3（包括0，不包括3）的数字依次赋值给returnMat的第index行的每一列
        class_label_vector.append(int(list_from_line[-1]))  #在列表末尾添加元素  -1表示获取listFromLine末尾的元素 且需要指定类型为int 否则python会作字符串处理
        index +=1

    return return_mat,class_label_vector

'''
该方法用于将特征数据进行归一化处理
归一化公式为:
newValue=(oldValue-minValue)/(maxValue-minValue)
minValue和maxValue分别表示数据集中的最大值和最小值

'''
def auto_normalize(data_set):
    min_values=data_set.min(0)  #获取最小值  参数0表示获取矩阵每一列的最小值  参数1表示获取矩阵每一行的最小值,不加参数则表示获取整个矩阵中的最小值
    max_values=data_set.max(0)  #获取最大值  同上
    ranges=max_values-min_values
    normal_data_set=zeros(shape(data_set))  #生成一个与dataSet同格式的0矩阵
    m=data_set.shape[0] #获取 第一维的长度,即行数
    normal_data_set=data_set-tile(min_values,(m,1)) # minValue为1*3的矩阵 在行方向重复minValue m次，列方向上重复1次 即生成一个与dataSet相同的矩阵
    normal_data_set=normal_data_set/tile(ranges,(m,1))
    return normal_data_set,ranges,min_values

'''
画图
'''
def get_figure():
    dating_data_mat, dating_labels = file2matrix("/Users/ting/Documents/51code/git/python/KNN/datingTestSet2.txt")
    normal_data_set, ranges, min_values=auto_normalize(dating_data_mat)
    print(normal_data_set)
    figure = plt.figure()
    ax = figure.add_subplot(111)  # 生成一张画布 将画布分割成1行1列 并将图像画在第一1块位置中（按从左到右，从上到下）  如果是234 则表示将画布分割成2行3列共六块，并将图像画在第4块位置

    #前两个参数 x,y 渲染x,y轴  X轴：datingDataMat[:, 1]，Y轴：datingDataMat[:, 2]
    #第三个参数 s 定义样本点图案的大小 15.0*array(datingLabels) 以标签值*15来定义每个样本点的大小，以此来区分出三种不同的标签，
    #第四个参数 c 定义样本点图案的颜色 RGB 15.0*array(datingLabels) 以标签值*15来定义每个样本点的颜色，以此来区分出三种不同的标签，
    ax.scatter(normal_data_set[:, 0], normal_data_set[:, 1],15.0*array(dating_labels),20.0*array(dating_labels))
    plt.title("玩视频游戏所耗费的时间比")
    print(normal_data_set)
    plt.show()



'''
测试分类器
'''
def dating_classify():
    ho_radio=0.10
    dating_data_mat, dating_labels = file2matrix("/Users/ting/Documents/51code/git/python/KNN/datingTestSet2.txt")
    normal_matrix, ranges, min_values = auto_normalize(dating_data_mat)
    m=normal_matrix.shape[0] #获取行数
    num_test_vector=int(ho_radio*m) #获取用来训练分类器的样本个数
    error_count=0
    for i in range(num_test_vector):
        classify_result=classify.classify0(normal_matrix[i,:],normal_matrix[num_test_vector:m,:],dating_labels[num_test_vector:m],4)
        print ("验证分类结果为 d%,实际分类为: d%",classify_result,dating_labels[i])
        if classify_result != dating_labels[i] :
            error_count+=1

    print("总的测试错误个数为:",error_count)
    return None


if __name__ == '__main__':
    dating_person_classify()

